package ai;

import java.io.IOException;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Observable;
import java.util.Observer;

import model.AbstractPlayer;
import model.Connected4Game;
import model.GameConfiguration;
import model.PlayerType;
import model.State;

public class StatisticsLauncher implements Observer {

	private Map<Connected4Game, GameStatistics> currentGameMap;
	private List<GameConfiguration> configList;
	private int parallelRun;
	private String savePath;
	private int configToLaunchIndex;
	private StatisticsManager statisticsManager;
	private String fileName;

	public StatisticsLauncher(List<GameConfiguration> config, int parallelRun,
			String savePath, String name) {
		this.fileName = name;
		statisticsManager = new StatisticsManager();
		this.configList = config;
		this.parallelRun = parallelRun;
		this.savePath = savePath;

		configToLaunchIndex = 0;
		currentGameMap = new HashMap<Connected4Game, GameStatistics>();

		startNextGames(this.parallelRun);
	}

	private void startNextGames(int parallelRun) {
		int started = 0;
		while (configToLaunchIndex < configList.size() && started < parallelRun) {
			createAndStartGame(configList.get(configToLaunchIndex));
			started++;
			configToLaunchIndex++;
		}
	}

	/**
	 * Crea e avvia il gioco, ne mantiene il riferimento insieme a quello delle
	 * relative statistiche nella mappa (di classe) currentGameMap
	 * 
	 * @param config
	 */

	private void createAndStartGame(GameConfiguration config) {
		Connected4Game game;
		GameStatistics stats;
		// inizializzo le informazioni sui giocatori e le statistiche
		String[] pInfo = createPlayersInfo(config);
		stats = new GameStatistics(pInfo[0], pInfo[1], config.getRows(),
				config.getCols());

		// Inizializzo i giocatori
		AbstractPlayer players[] = config.createAbstractPlayers(null);
		// Inizializzo il gioco.
		game = new Connected4Game(config.getRows(), config.getCols(),
				players[0], players[1], true);
		game.addObserver(this);

		game.start();

		System.out.println("Avviata partita n." + (configToLaunchIndex + 1)
				+ "/" + configList.size());

		currentGameMap.put(game, stats);
	}

	private String[] createPlayersInfo(GameConfiguration config) {
		String[] pInfo = new String[2];
		StringBuilder sb = new StringBuilder();

		String nl = System.getProperty("line.separator");
		// player 1 info
		sb.append("Player1: " + config.getPlayer1() + " Depth: "
				+ config.getDepth1());
		sb.append(nl
				+ "Heuristics: "
				+ getHeuristicsString(config.isEnThreatFinder1(),
						config.isEnTB1(), config.isEnDT1(),
						config.isEnTactics1(), config.isEnPotentialThreat1()));
		sb.append(nl + "Choice: " + config.getChoiceType1());
		sb.append(nl + "Reordering: " + config.isEnReordering1());

		pInfo[0] = sb.toString();

		// player 2 info
		sb = new StringBuilder();

		sb.append("Player2: " + config.getPlayer2() + " Depth: "
				+ config.getDepth2());
		sb.append(nl
				+ "Heuristics: "
				+ getHeuristicsString(config.isEnThreatFinder2(),
						config.isEnTB2(), config.isEnDT2(),
						config.isEnTactics2(), config.isEnPotentialThreat2()));
		sb.append(nl + "Choice: " + config.getChoiceType2());
		sb.append(nl + "Reordering: " + config.isEnReordering2());

		pInfo[1] = sb.toString();

		return pInfo;
	}

	private String getHeuristicsString(boolean isEnThreatFinder,
			boolean isEnTB, boolean isEnDT, boolean isEnTactics,
			boolean isEnPotentialThreat) {
		String s = "";
		if (isEnThreatFinder)
			s += "TF ";
		if (isEnTB)
			s += "TB ";
		if (isEnDT)
			s += "DT ";
		if (isEnTactics)
			s += "TA ";
		if (isEnPotentialThreat)
			s += "PT ";

		return s;
	}

	@Override
	public void update(Observable o, Object arg) {
		List<List<Double>> timeStats;

		Connected4Game game = (Connected4Game) o;

		// a fine partita ottengo le statistiche
		if (game.getGameState() != State.PLAYING) {
			GameStatistics stats = null;
			while (stats == null) {
				stats = currentGameMap.get(game);
				if (stats == null)
					System.out.println("STATS NULL");
			}

			timeStats = game.getStatistics();
			String[] times = getTimeStatsString(timeStats);
			stats.setGameState(game.getGameState());
			stats.setPlayer1Times(times[0]);
			stats.setPlayer2Times(times[1]);
			stats.setTime1(timeStats.get(0));
			stats.setTime2(timeStats.get(1));
			statisticsManager.registerStatistic(stats);

			// nome file: Player1vsPlayer2_rows_cols progressiveNumber.txt
			// stats.saveOnFile(
			// savePath,
			// game.getP1().getDescription() + "vs"
			// + game.getP1().getDescription() + "_"
			// + stats.getBoardRows() + "_" + stats.getBoardCols()
			// + " " + (++progressive) + ".txt");
			currentGameMap.remove(game);
			System.out.println("Terminata partita");
			startNextGames(1);

			if (currentGameMap.isEmpty()) {
				statisticsManager.saveStatisticsOnFiles(savePath, fileName);
				// systemShutdown();
			}

		}
	}

	public void systemShutdown() {
		try {
			Runtime.getRuntime().exec("shutdown -s -t 30");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public String[] getTimeStatsString(List<List<Double>> timeStats) {
		String[] playerTimes = new String[2];
		DecimalFormat df = new DecimalFormat("###.#####");

		for (int i = 0; i < playerTimes.length; i++) {
			playerTimes[i] = "";
			List<Double> list = timeStats.get(i);
			for (Double val : list) {
				playerTimes[i] += df.format(val)
						+ System.getProperty("line.separator");
			}
		}
		return playerTimes;
	}

	public static void main(String[] args) {
		List<GameConfiguration> configs = new ArrayList<GameConfiguration>();

		// TEST SU TABELLA STANDARD
		int rows = 7;
		int cols = 8;

		/**
		 * ABPruning - Minimax - ABPruningReordered senza Euristiche
		 */
		for (int j = 0; j < 2; j++) {
			// Minimax 1-8 vs Minimax 1-8
			for (int i = 9; i <= 9; i++) {
				configs.add(new GameConfiguration(rows, cols,
						PlayerType.MINIMAX, PlayerType.MINIMAX, i, i, false,
						false, false, false, false, false, false, false, false,
						false, false, false, BestChoiceType.CENTERED,
						BestChoiceType.CENTERED, false, false));
			}

			// ABPruning 1-10 vs ABPruning 1-10
			for (int i = 3; i <= 10; i++) {
				configs.add(new GameConfiguration(rows, cols,
						PlayerType.ABPRUNING, PlayerType.ABPRUNING, i, i,
						false, false, false, false, false, false, false, false,
						false, false, false, false, BestChoiceType.CENTERED,
						BestChoiceType.CENTERED, false, false));
			}
			// ABPruningReo 1-10 vs ABPruningReo 1-10

			for (int i = 3; i <= 10; i++) {
				configs.add(new GameConfiguration(rows, cols,
						PlayerType.ABPRUNING, PlayerType.ABPRUNING, i, i,
						false, false, false, false, false, false, false, false,
						false, false, false, false, BestChoiceType.CENTERED,
						BestChoiceType.CENTERED, true, true));
			}
		}

		/**
		 * ABPruning - Minimax - ABPruningReordered senza Euristiche
		 */
		for (int j = 0; j < 10; j++) {
			// Minimax 1-8 vs Minimax 1-8
			for (int i = 3; i <= 9; i++) {
				configs.add(new GameConfiguration(rows, cols,
						PlayerType.MINIMAX, PlayerType.MINIMAX, i, i, true,
						true, true, true, true, true, true, true, true, true,
						true, true, BestChoiceType.CENTERED,
						BestChoiceType.CENTERED, false, false));
			}

			// ABPruning 1-10 vs ABPruning 1-10
			for (int i = 3; i <= 9; i++) {
				configs.add(new GameConfiguration(rows, cols,
						PlayerType.ABPRUNING, PlayerType.ABPRUNING, i, i, true,
						true, true, true, true, true, true, true, true, true,
						true, true, BestChoiceType.CENTERED,
						BestChoiceType.CENTERED, false, false));
			}
			// ABPruningReo 1-10 vs ABPruningReo 1-10

			for (int i = 3; i <= 9; i++) {
				configs.add(new GameConfiguration(rows, cols,
						PlayerType.ABPRUNING, PlayerType.ABPRUNING, i, i, true,
						true, true, true, true, true, true, true, true, true,
						true, true, BestChoiceType.CENTERED,
						BestChoiceType.CENTERED, true, true));
			}
		}

		/**
		 * ABPruningReo - ABPruningReo+TF - ABPruningReo+TF+TP
		 */

		// ABPruningReo 1-10 vs ABPruningReo 1-10
		for (int i = 3; i <= 8; i++) {
			configs.add(new GameConfiguration(rows, cols, PlayerType.ABPRUNING,
					PlayerType.ABPRUNING, i, i, false, false, false, false,
					false, false, false, false, false, false, false, false,
					BestChoiceType.CENTERED, BestChoiceType.CENTERED, true,
					true));
		}
		// ABPruningReo+TF 1-10 vs ABPruningReo+TF 1-10
		for (int i = 3; i <= 8; i++) {
			configs.add(new GameConfiguration(rows, cols, PlayerType.ABPRUNING,
					PlayerType.ABPRUNING, i, i, true, true, true, true, false,
					false, true, true, true, true, true, true,
					BestChoiceType.CENTERED, BestChoiceType.CENTERED, true,
					true));
		}
		// ABPruningReo+TF+PT CEN 1-10 vs ABPruningReo+TF+PT RAN 1-10
		for (int i = 3; i <= 8; i++) {
			configs.add(new GameConfiguration(rows, cols, PlayerType.ABPRUNING,
					PlayerType.ABPRUNING, i, i, true, true, true, true, true,
					true, true, true, true, true, true, true,
					BestChoiceType.CENTERED, BestChoiceType.CENTERED, true,
					true));
		}
		for (int j = 0; j < 10; j++) {

			/**
			 * Statistiche sul BestChoiceType
			 */
			// ABPruningReo+TF+PT CEN 1-10 vs ABPruningReo+TF+PT RAN 1-10
			for (int i = 3; i <= 8; i++) {
				configs.add(new GameConfiguration(rows, cols,
						PlayerType.ABPRUNING, PlayerType.ABPRUNING, i, i, true,
						true, true, true, true, true, true, true, true, true,
						true, true, BestChoiceType.CENTERED,
						BestChoiceType.RANDOM, true, true));
			}
			for (int i = 3; i <= 8; i++) {
				configs.add(new GameConfiguration(rows, cols,
						PlayerType.ABPRUNING, PlayerType.ABPRUNING, i, i, true,
						true, true, true, true, true, true, true, true, true,
						true, true, BestChoiceType.RANDOM,
						BestChoiceType.CENTERED, true, true));
			}
			// ABPruningReo+TF+PT CEN 1-10 vs ABPruningReo+TF+PT GAU 1-10
			for (int i = 3; i <= 8; i++) {
				configs.add(new GameConfiguration(rows, cols,
						PlayerType.ABPRUNING, PlayerType.ABPRUNING, i, i, true,
						true, true, true, true, true, true, true, true, true,
						true, true, BestChoiceType.CENTERED,
						BestChoiceType.GAUSSIAN, true, true));
			}
			for (int i = 3; i <= 8; i++) {
				configs.add(new GameConfiguration(rows, cols,
						PlayerType.ABPRUNING, PlayerType.ABPRUNING, i, i, true,
						true, true, true, true, true, true, true, true, true,
						true, true, BestChoiceType.GAUSSIAN,
						BestChoiceType.CENTERED, true, true));
			}
			// ABPruningReo+TF+PT GAU 1-10 vs ABPruningReo+TF+PT RAN 1-10
			for (int i = 3; i <= 8; i++) {
				configs.add(new GameConfiguration(rows, cols,
						PlayerType.ABPRUNING, PlayerType.ABPRUNING, i, i, true,
						true, true, true, true, true, true, true, true, true,
						true, true, BestChoiceType.GAUSSIAN,
						BestChoiceType.RANDOM, true, true));
			}
			for (int i = 3; i <= 8; i++) {
				configs.add(new GameConfiguration(rows, cols,
						PlayerType.ABPRUNING, PlayerType.ABPRUNING, i, i, true,
						true, true, true, true, true, true, true, true, true,
						true, true, BestChoiceType.RANDOM,
						BestChoiceType.GAUSSIAN, true, true));
			}

		}
		for (int j = 0; j < 10; j++) {
			/**
			 * EURISTICHE
			 */
			// ABPruningReo+TFonly CEN 1-10 vs ABPruningReo CEN 1-10

			for (int i = 3; i <= 10; i++) {
				configs.add(new GameConfiguration(rows, cols,
						PlayerType.ABPRUNING, PlayerType.ABPRUNING, i, i, true,
						true, true, false, false, false, false, false, false,
						false, false, false, BestChoiceType.CENTERED,
						BestChoiceType.CENTERED, true, true));
			}
			for (int i = 3; i <= 10; i++) {
				configs.add(new GameConfiguration(rows, cols,
						PlayerType.ABPRUNING, PlayerType.ABPRUNING, i, i, true,
						true, false, true, false, false, false, false, false,
						false, false, false, BestChoiceType.CENTERED,
						BestChoiceType.CENTERED, true, true));
			}

			// ABPruningReo+TFcompleta CEN 1-10 vs ABPruningReo+TFonly CEN 1-10
			for (int i = 3; i <= 10; i++) {
				configs.add(new GameConfiguration(rows, cols,
						PlayerType.ABPRUNING, PlayerType.ABPRUNING, i, i, true,
						true, true, true, false, false, true, false, true,
						false, true, false, BestChoiceType.CENTERED,
						BestChoiceType.CENTERED, true, true));
			}
			for (int i = 3; i <= 10; i++) {
				configs.add(new GameConfiguration(rows, cols,
						PlayerType.ABPRUNING, PlayerType.ABPRUNING, i, i, true,
						true, true, true, false, false, false, true, false,
						true, false, true, BestChoiceType.CENTERED,
						BestChoiceType.CENTERED, true, true));
			}

			// ABPruningReo+TFcompleta+PT CEN 1-10 vs ABPruningReo+TFcompleta
			// CEN
			// 1-10
			for (int i = 3; i <= 10; i++) {
				configs.add(new GameConfiguration(rows, cols,
						PlayerType.ABPRUNING, PlayerType.ABPRUNING, i, i, true,
						true, true, true, true, false, true, true, true, true,
						true, true, BestChoiceType.CENTERED,
						BestChoiceType.CENTERED, true, true));
			}
			for (int i = 3; i <= 10; i++) {
				configs.add(new GameConfiguration(rows, cols,
						PlayerType.ABPRUNING, PlayerType.ABPRUNING, i, i, true,
						true, true, true, false, true, true, true, true, true,
						true, true, BestChoiceType.CENTERED,
						BestChoiceType.CENTERED, true, true));
			}
		}
		// new StatisticsLauncher(configs, 1, "D:\\Connected4Stats\\6x7bis\\",
		// 199);
		new StatisticsLauncher(
				configs,
				2,
				"/Users/Francesco/Dropbox/Rafe??/Intelligenza Artificiale/Statistiche/",
				"ReoAdvMM_7x8bis");

	}
}
